<template>
	<div ref="statsRef" class="statistics"></div>
	<div ref="containerRef" class="container"></div>
</template>

<script lang="ts" setup>
import {onMounted, ref} from 'vue';
import {AxesHelper, Color, MeshBasicMaterial, PerspectiveCamera, PlaneGeometry, Scene, WebGLRenderer, Mesh, BoxGeometry, SphereGeometry, SpotLight, MeshLambertMaterial, MeshPhongMaterial, DoubleSide, PointLight, AmbientLight, Fog, BufferGeometry, CylinderGeometry, OctahedronGeometry, TetrahedronGeometry, TorusGeometry} from 'three';
import Stats from 'stats.js';
import * as dat from 'dat.gui';
import { createMultiMaterialObject } from 'three/examples/jsm/utils/SceneUtils';

const stats = new Stats();

// stats.dom.style.top = '50px';
stats.showPanel(0);
	
const containerRef = ref<HTMLDivElement>();
const statsRef = ref<HTMLDivElement>();
const scene = new Scene();
const camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

camera.position.set(-30, 40, 30);
camera.lookAt(scene.position);

const renderer = new WebGLRenderer();

// renderer.setClearColor(new Color(0xeeeeee));
renderer.setClearColor(0xeeeeee);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;

const cubeGeometry = new BoxGeometry(4, 4, 4);

const cubeMaterial = new MeshLambertMaterial({
// const cubeMaterial = new MeshBasicMaterial({
	color: 0xff0000,
	wireframe: false,
});

const cube = new Mesh(cubeGeometry, cubeMaterial);

cube.castShadow = true;
cube.position.set(2, 2, 2);
scene.add(cube);

/**
* 光源设置
*/
//聚光灯
const spotLight = new SpotLight(0xffffff, 3000);

spotLight.position.set(-20, 30, -10);
spotLight.castShadow = true;
scene.add(spotLight);

const axes = new AxesHelper(20);

scene.add(axes);

const planeGeometry = new PlaneGeometry(60, 20);
const meshBasicMaterial = new MeshLambertMaterial({color: 0xcccccc});
// const meshBasicMaterial = new MeshBasicMaterial({color: 0xcccccc});
const plane = new Mesh(planeGeometry, meshBasicMaterial);

plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
scene.add(plane);

const sphereGeometry = new SphereGeometry(4);

const sphereMaterial = new MeshLambertMaterial({
// const sphereMaterial = new MeshBasicMaterial({
	color: 0x7777ff,
	wireframe: false,
});

const sphere = new Mesh(sphereGeometry, sphereMaterial);

sphere.castShadow = true;
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
scene.add(sphere);

const geoms: BufferGeometry[] = [];

geoms.push(new CylinderGeometry(1, 4, 8));
geoms.push(new BoxGeometry(2, 2, 2));
geoms.push(new OctahedronGeometry(3));
geoms.push(new TetrahedronGeometry(3));
geoms.push(new TorusGeometry(3, 1, 10, 10));

const materials = [
	new MeshLambertMaterial({
		color: Math.random() * 0xffffff,
		flatShading: true,
	}),
	new MeshBasicMaterial({
		color: 0x000000,
		wireframe: true,
	})
];

geoms.forEach((g, i) => {
	const mesh = createMultiMaterialObject(g, materials);
	
	mesh.castShadow = true;
	mesh.position.x = -24 + i * 10;
	mesh.position.y = 4;
	scene.add(mesh);
});

const controlRef = ref({
	rotationSpeed: 0.02,
	bouncingSpeed: 0.03,
	numberOfObjects: 0,
	addCube: function(){
		const cubeGeometry = new BoxGeometry(4, 4, 4);
		const cube = new Mesh(cubeGeometry, cubeMaterial);
		
		cube.name = 'cube-' + scene.children.length;
		cube.castShadow = true;
		cube.position.x = -30 + Math.round(Math.random() * 60);
		cube.position.y = Math.round(Math.random() * 5);
		cube.position.z = -20 + Math.round(Math.random() * 40);
		scene.add(cube);
		this.numberOfObjects = scene.children.length;
	},
	remove: function(){
		const allChildren = scene.children;
		const lastObject = allChildren[allChildren.length - 1];
		
		if(lastObject instanceof Mesh && lastObject.name.startsWith('cube')){
			//mesh 网格模型，且名称以 cube 开头，新添加的立方体
			scene.remove(lastObject);
		}
		
		this.numberOfObjects = scene.children.length;
	},
	addFog: function(){
		scene.fog = new Fog(0xffffff, 0.015, 100);
		this.numberOfObjects = scene.children.length;
	},
	removeFog: function(){
		scene.fog = null;
		this.numberOfObjects = scene.children.length;
	},
	toggleMaterial: function(){
		if(!scene.overrideMaterial){
			//初始材质状态
			scene.overrideMaterial = new MeshLambertMaterial({
				color: 0xffffff,
			});
		}else{
			//重写材质状态
			scene.overrideMaterial = null;
		}
		
		this.numberOfObjects = scene.children.length;
	},
});

if(document.querySelectorAll('.dg.ac>.dg.main.a').length === 0){
	//还未初始化图形用户界面
	window.gui = new dat.GUI();
	window.gui.add(controlRef.value, 'rotationSpeed', 0, 0.5);
	window.gui.add(controlRef.value, 'bouncingSpeed', 0, 0.5);
	window.gui.add(controlRef.value, 'numberOfObjects').listen();
	window.gui.add(controlRef.value, 'addCube');
	window.gui.add(controlRef.value, 'remove');
	window.gui.add(controlRef.value, 'addFog');
	window.gui.add(controlRef.value, 'removeFog');
	window.gui.add(controlRef.value, 'toggleMaterial');
}

let step = 0;

function renderScene(){
	stats.update();
	
	scene.traverse((e) => {
		/* if(e instanceof Mesh && e != plane){
			//mesh 网格模型，且不是平面
			e.rotation.x += controlRef.value.rotationSpeed;
			e.rotation.y += controlRef.value.rotationSpeed;
			e.rotation.z += controlRef.value.rotationSpeed;
		} */
		
		if(e.name.startsWith('cube')){
			//名称以 cube 开头，新添加的立方体
			e.rotation.x += controlRef.value.rotationSpeed;
			e.rotation.y += controlRef.value.rotationSpeed;
			e.rotation.z += controlRef.value.rotationSpeed;
		}
	});
	
	cube.rotation.x += controlRef.value.rotationSpeed;
	cube.rotation.y += controlRef.value.rotationSpeed;
	cube.rotation.z += controlRef.value.rotationSpeed;
	// cube.scale.x += 0.01;
	step += controlRef.value.bouncingSpeed;
	sphere.position.x = 20 + 10 * Math.cos(step);
	sphere.position.y = 2 + 10 * Math.abs(Math.sin(step));
	requestAnimationFrame(renderScene);
	renderer.render(scene, camera);
}

renderScene();

window.addEventListener('resize', () => {
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize(window.innerWidth, window.innerHeight);
}, false);

onMounted(() => {
	statsRef.value?.append(stats.dom);
	containerRef.value?.appendChild(renderer.domElement);
	renderer.render(scene, camera);
});
</script>

<style scoped>
</style>